package redecolaborativa.server.interfaceUsuario;



import java.util.*;

import redecolaborativa.server.enums.TipoDeMensagem;
import redecolaborativa.server.excecoes.*;
import redecolaborativa.server.objetos.Emprestimo;
import redecolaborativa.server.objetos.Item;
import redecolaborativa.server.sistema.Gerenciador;
import redecolaborativa.server.sistema.Sessao;
import redecolaborativa.server.sistema.Usuario;
import redecolaborativa.server.util.Visualizador;

import easyaccept.script.test.TestFacade;

/**
 * 
 * @author Isabelly Rocha, Lilia Sampaio e Pedro Adelino
 *
 */
public class Fachada extends TestFacade {
	private final static int USUARIO_SESSAO = 0;
	private final static int USUARIO_PROCURADO = 1;
	private static Fachada fachada;
	private Gerenciador gerenciador;
	
	private Fachada() {
		gerenciador = Gerenciador.getInstance();
	}
	
	/**
	 * Zera o sistema.
	 */
	public void zerarSistema() {
		gerenciador.reset();
	}
	
	/**
	 * Encerra o sistema.
	 */
	public void encerrarSistema() {
		gerenciador.encerrar();
	}
	
	/**
	 * Adiciona dias a data do sistema.
	 * @param dias Quantidade de dias
	 */
	public void adicionarDias(int dias) {
		gerenciador.adicionarDias(dias);
	}
	
	/**
	* Metodo que cadastra um usuario
	 * @param nome Nome do Usuario
	 * @param login Login do Usuario
	 * @param endereco Endereco doo Usuario 
	 * @throws Exception
	 */
	public void criarUsuario(String login, String nome, String endereco) throws Exception{
		gerenciador.cadastrarUsuario(nome, login, endereco);
	}

	/**
	 * Abri a sessao do usuario
	 * @param login do usuario
	 * @return Id da sessao do usuario
	 * @throws Exception
	 */
	public String abrirSessao(String login) throws Exception {
		Sessao sessaoAberta = gerenciador.abrirSessao(login);
		return String.valueOf(sessaoAberta.getId());
	}
	
	/**
	 * Metodo que retorna os usuarios e os respectivos logins
	 * @return String contnedo os usuarios e os respectivos logins
	 */
	public String retornaUsuariosESeusLogins(){
		 return gerenciador.retornaUsuariosESeusLogins();
	}

	/**
	 * Retorna um atributo do usuario atraves de uma pesquisa
	 * @param login do usuario
	 * @param atributo nome ou endereco
	 * @return String contendo o resultado da pesquisa
	 * @throws Exception
	 */
	public String getAtributoUsuario(String login, String atributo) throws Exception {
		return gerenciador.getAtributoDeUmUsuario(login, atributo);
	}

	/**
	 * Cadastra um item 
	 * @param idSessao Id da sessao do usuario
	 * @param nome Nome do item
	 * @param descricao Descricao do item
	 * @param categoria Categorias do item
	 * @return O id do item
	 * @throws Exception
	 */
	public String cadastrarItem(String idSessao, String nome, String descricao, String categoria) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		logado.cadastrarItem(nome, categoria, descricao);
		Item ultimoItemCadastrado = logado.gerenteDeItens().getItens().get(logado.gerenteDeItens().getItens().size() - 1);
		int idItem = ultimoItemCadastrado.getID();
		return String.valueOf(idItem);
	}

	/**
	 * Retorna um atributo do item atravez de uma pesquisa
	 * @param idItem Id do Item
	 * @param atributo Nome, descricao ou categoria
	 * @return String contendo o resultado da pesquisa
	 * @throws Exception
	 */
	public String getAtributoItem(String idItem, String atributo) throws Exception  {
		 return gerenciador.getAtributoItem(idItem, atributo);
	}
	
	/**
	 * Requisita a amizade de um usuario.
	 * @param idSessao Id da sessao do usuario
	 * @param login Login do usuario que voce quer requisitar amizade
	 * @throws Exception
	 */
	public void requisitarAmizade(String idSessao, String login) throws Exception  {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		if (!usuarios.isEmpty()) {
			Usuario usuarioSessao = usuarios.get(USUARIO_SESSAO);
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			usuarioSessao.requisitarAmizade(usuarioProcurado);
		}
	}
	
	/**
	 * Desfaz uma amizade
	 * @param idSessao Id da sessao do usuario
	 * @param login Login do amigo que se quer desfazer a amizade
	 * @throws Exception
	 */
	public void desfazerAmizade(String idSessao, String login) throws Exception  {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		if (!usuarios.isEmpty()) {
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			usuarios.get(USUARIO_SESSAO).desfazerAmizade(usuarioProcurado);
		}
	}

	/**
	 * Aprova uma solicitacao de amizade
	 * @param idSessao Id da sessao do usuario
	 * @param login Login do usuario que voce quer aceitar a requisicao de amizade 
	 * @throws Exception
	 */
	public void aprovarAmizade(String idSessao, String login) throws Exception  {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		if (!usuarios.isEmpty()) {
			Usuario usuarioSessao = usuarios.get(USUARIO_SESSAO);
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			usuarioSessao.aprovarAmizade(usuarioProcurado);
		}
	}
	
	/**
	 * Rejeita uma solicitacao de amizade
	 * @param idSessao Id da sessao do usuario
	 * @param nome Nome do usuario que voce quer rejeitar a requisicao de amizade
	 * @throws SessaoInvalidaException
	 * @throws LoginInvalidoException
	 */
	public void rejeitarAmizade(String idSessao, String login) throws SessaoInvalidaException, LoginInvalidoException {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		if(!usuarios.isEmpty()){
			Usuario usuarioSessao = usuarios.get(USUARIO_SESSAO);
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			usuarioSessao.rejeitarAmizade(usuarioProcurado);
		}	
	}
	
	/**
	 * Verifica se um usuario eh amigo
	 * @param idSessao Id da sessao do usuario
	 * @param login Login do usuario que voce quer ver se eh amigo
	 * @return True caso seja amigo ou false caso nao seja
	 * @throws LoginInvalidoException 
	 * @throws SessaoInvalidaException 
	 * @throws Exception
	 */
	public boolean ehAmigo(String idSessao, String login) throws Exception  {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		if (!usuarios.isEmpty()) {
			Usuario usuarioSessao = usuarios.get(USUARIO_SESSAO);
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			return usuarioSessao.gerenteDeAmigos().contem(usuarioProcurado);
		} return false;
	}

	/**
	 * Visualiza os amigos
	 * @param idSessao Id da sessao do usuario
	 * @return Uma string contendo os amigos.
	 * @throws SessaoInvalidaException
	 * @throws Exception
	 */
	public String getAmigos(String idSessao) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.visualizaAmigos(logado);
	}	
	
	/**
	 * Retorna uma string contendo todos os amigos no usuario daquele idSessao especificado.
	 * @param idSessao Id da sessao do usuario
	 * @param login Login do usuario procurado
	 * @return String contendo os amigos do usuario procurado
	 * @throws LoginInvalidoException 
	 * @throws SessaoInvalidaException 
	 * @throws Exception
	 */
	public String getAmigos(String idSessao, String login) throws Exception {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		String retorno = "";
		if (!usuarios.isEmpty()) {
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			retorno = Visualizador.visualizaAmigos(usuarioProcurado);
		} return retorno;
	}
	
	/**
	 * Visualiza os amigos e seus logins
	 * @param idSessao Id da sessao do usuario
	 * @return Uma string contendo os amigos e os logins
	 * @throws SessaoInvalidaException
	 */
	public String getAmigosComLogin(String idSessao) throws SessaoInvalidaException{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.visualizaAmigosELogin(logado);
	}

	/**
	 * Visualiza os itens
	 * @param idSessao Id da sessao do usuario
	 * @return Uma string contendo os itens
	 * @throws SessaoInvalidaException 
	 * @throws PermissaoNegadaException 
	 * @throws Exception
	 */
	public String getItens(String idSessao) throws Exception  {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.visualizaItens(logado, logado);
	}
	
	/**
	 * Retorna os itens
	 * @param idSessao Id da sessao do usuario
	 * @param login Login de outro usuario
	 * @return Uma string contendo os itens de um outro usuario
	 * @throws LoginInvalidoException 
	 * @throws SessaoInvalidaException 
	 * @throws PermissaoNegadaException 
	 * @throws Exception
	 */
	public String getItens(String idSessao, String login) throws SessaoInvalidaException, LoginInvalidoException, PermissaoNegadaException  {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, login);
		String retorno = "";
		if (!usuarios.isEmpty()) {
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			retorno = Visualizador.visualizaItens(usuarios.get(USUARIO_SESSAO), usuarioProcurado);
		} return retorno;
	}
	
	/**
	 * Retorna os itens com seu ids
	 * @param idSessao Id da sessao do usuario
	 * @param loginAmigo Login do amigo
	 * @return Uma string contendo os itens e os ids
	 * @throws SessaoInvalidaException
	 * @throws LoginInvalidoException
	 * @throws PermissaoNegadaException
	 */
	public String getItensComId(String idSessao, String loginAmigo) throws SessaoInvalidaException, LoginInvalidoException, PermissaoNegadaException {
		List<Usuario> usuarios = gerenciador.localizaUsuariosLogadoEProcurado(idSessao, loginAmigo);
		String retorno = "";
		if (!usuarios.isEmpty()) {
			Usuario usuarioProcurado = usuarios.get(USUARIO_PROCURADO);
			retorno = Visualizador.visualizaItensComIdDeAmigo(usuarios.get(USUARIO_SESSAO), usuarioProcurado);
		} return retorno;
	}
	
	/**
	 * Metodo que mostra os nomes e os ids dos itens do usuario
	 * @param idSessao Id da sessao do usuario
	 * @return String contendo os nomes e os ids dos itens do usuario
	 * @throws SessaoInvalidaException
	 */
	public String mostraItensComId(String idSessao) throws SessaoInvalidaException{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.visualizaItensComId(logado);
	}
	
	/**
	 * Retorna o id do item
	 * @param nomeItem Nome do item
	 * @return Uma string informando o id do item
	 * @throws Exception
	 */
	public String itemID(String nomeItem) throws Exception {
		return gerenciador.localizaItemPorNome(nomeItem);
	}
	
	/**
	 * Metodo que visualiza os itens adquiridos pelo usuario
	 * @param idSessaoAberta Id da sessao do usuario
	 * @return String contendo os itens adquiridos pelo usuario
	 * @throws SessaoInvalidaException
	 */
	public String retornaItensAdquiridos(String idSessaoAberta) throws SessaoInvalidaException{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessaoAberta);
		return logado.gerenteDeEmprestimos().retornaItensAdquiridos();
		
	}

	/**
	 * Visualiza os emprestimos
	 * @param idSessao Id da sessao do usuario
	 * @param tipoEmprestimo Tipo do emprestimo
	 * @return Uma string contendo os emprestimo
	 * @throws Exception
	 */
	public String getEmprestimos(String idSessao, String tipoEmprestimo) throws Exception  {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return retornaEmprestimos(logado, tipoEmprestimo);
	}
	
	/**
	 * Retorna os emprestimos de acordo com o tipo especificado
	 * @param usuario Usuario
	 * @param tipoEmprestimo Se emprestador, beneficiado ou todos
	 * @return A string contendo os emprestimos
	 * @throws TipoInvalidoException
	 */
	private String retornaEmprestimos(Usuario usuario, String tipoEmprestimo) throws TipoInvalidoException {
		String retorno = "";
		if(tipoEmprestimo == null || tipoEmprestimo.trim().equals("")) {
			throw new TipoInvalidoException("Tipo inválido");
		} if(!(tipoEmprestimo.equals("emprestador") || tipoEmprestimo.equals("beneficiado") || tipoEmprestimo.equals("todos"))){
			throw new TipoInvalidoException("Tipo inexistente");
		} if(tipoEmprestimo.equals("emprestador")) {
			retorno = Visualizador.visualizaEmprestimos(usuario);
		} if(tipoEmprestimo.equals("beneficiado")) {
			retorno = Visualizador.visualizaItensAquiridos(usuario);
		} if(tipoEmprestimo.equals("todos")) {
			retorno = Visualizador.concatenaEmprestimos(usuario);
		} return retorno;
	}
	
	/**
	 * Requisita um emprestimo pelo id do item
	 * @param idSessao Id da sessao do usuario
	 * @param idItem Id do item que quer emprestado
	 * @param duracao Duracao do emprestimo
	 * @return O id da requisicao do emprestimo
	 * @throws Exception
	 */
	public String requisitarEmprestimo(String idSessao, String idItem, int duracao) throws Exception  {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Item itemProc = gerenciador.localizaItemPorID(idItem);
		logado.requisitarItem(itemProc, duracao);
		List<Emprestimo> solicitacoesEmprestimo = itemProc.getDono().gerenteDeEmprestimos().getSolicitacoesDeEmprestimo();
		return String.valueOf(solicitacoesEmprestimo.get(solicitacoesEmprestimo.size() - 1).getIDRequisicao());
	}
	
	/**
	 * Aprova um pedido de emprestimo pelo id da requisicao do emprestimo
	 * @param idSessao Id da sessa do usuario
	 * @param idRequisicaoEmprestimo O id da requisicao do emprestimo
	 * @return O id do emprestimo
	 * @throws Exception
	 */
	public String aprovarEmprestimo(String idSessao, String idRequisicaoEmprestimo) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Emprestimo emprestimo = gerenciador.pesquisaRequisicaoEmprestimoId(idRequisicaoEmprestimo);
		logado.aceitarSolicitacaoDeItem(emprestimo.getItem());
		return String.valueOf(emprestimo.getIDEmprestimo());
	}
	
	/**
	 * Rejeita o pedido de emprestimo pelo nome do item
	 * @param idSessao Id da sessao do usuario
	 * @param nomeItem Nome do item que quer rejeitar
	 * @throws SessaoInvalidaException
	 * @throws RequisicaoInvalidaException
	 */
	public void rejeitarEmprestimo(String idSessao, String idRequisicaoEmprestimo) throws SessaoInvalidaException, RequisicaoInvalidaException {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Emprestimo emprestimo = gerenciador.pesquisaRequisicaoEmprestimoId(idRequisicaoEmprestimo);
		logado.rejeitarSolicitacaoDeItem(emprestimo.getItem()); 
	}
	
	/**
	 * Devolve um item ao seu dono
	 * @param idSessao Id da sessao do usuario
	 * @param idEmprestimo Id do emprestimo do item
	 * @throws Exception
	 */
	public void devolverItem(String idSessao, String idEmprestimo) throws Exception{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Emprestimo emprestimo = gerenciador.pesquisaEmprestimoPorId(idEmprestimo);
		
		if(!emprestimo.getBeneficiado().equals(logado)) {
			throw new RequisicaoInvalidaException("O item só pode ser devolvido pelo usuário beneficiado");
		} logado.devolverItem(emprestimo.getItem());
	}
	
	/**
	 * Confirma o termino de um emprestimo pelo id do emprestimo
	 * @param idSessao Id da sessao do usuario
	 * @param idEmprestimo Id do emprestimo do item
	 * @throws Exception
	 */
	public void confirmarTerminoEmprestimo(String idSessao, String idEmprestimo) throws Exception{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Emprestimo emprestimo = gerenciador.pesquisaEmprestimoPorId(idEmprestimo);
		if(!emprestimo.getItem().getDono().equals(logado)) {
			throw new RequisicaoInvalidaException("O término do empréstimo só pode ser confirmado pelo dono do item");
		} 
		logado.confirmaDevolucao(emprestimo.getItem());
	}
	
	/**
	 * Requisita a devolucao do item.
	 * @param idSessao Id da sessao do item
	 * @param idEmprestimo Id do emprestimo do item
	 * @throws Exception
	 */
	public void requisitarDevolucao(String idSessao, String idEmprestimo) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Emprestimo emprestimo = gerenciador.pesquisaEmprestimoPorId(idEmprestimo);
		if(!emprestimo.getItem().getDono().equals(logado)) {
			throw new RequisicaoInvalidaException("O usuário não tem permissão para requisitar a devolução deste item");
		} 
		logado.requisitarDevolucaoDeItem(emprestimo.getItem()); 
	}
	
	/**
	 * Registrar o interesse em um item
	 * @param idSessao Id da sessao do usuario
	 * @param idItem Id do item que voce quer registrar um interesse
	 * @throws Exception
	 */
	public void registrarInteresse(String idSessao, String idItem) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Item itemProc = gerenciador.localizaItemPorID(idItem);
		logado.registraInteresseEmItem(itemProc);
	}
	
	/**
	 * Apaga um item 
	 * @param idSessao Id da sessao do usuario
	 * @param idItem Id do item que quer apagar
	 * @return True caso apagou, false caso contrario
	 * @throws Exception
	 */
	public void apagarItem(String idSessao, String idItem) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Item itemProc = gerenciador.localizaItemPorID(idItem);
		if(!logado.equals(itemProc.getDono())) {
			throw new DonoInvalidoException("O usuário não tem permissão para apagar este item");
		} 
		logado.apagarItem(itemProc);
	}
	
	/**
	 * Pesquisa um Item
	 * @param idSessao Id da sessao do usuario
	 * @param chave Chave da pesquisa
	 * @param atributo Se nome, descricao ou tipo
	 * @param tipoOrdenacao Crescente ou decrescente
	 * @param criterio Data Criacao ou Reputacao
	 * @return Uma string contendo os resultados encontrados
	 * @throws Exception
	 */
	public String pesquisarItem(String idSessao, String chave, String atributo, String tipoOrdenacao, String criterio) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return logado.pesquisaItem(chave, atributo, tipoOrdenacao, criterio);
	}
	
	/**
	 * Localiza um usuario
	 * @param idSessao Id da sessao do usuario
	 * @param chave Uma chave para a pesquisa
	 * @param atributo Se nome ou endereço
	 * @return Uma string contendo os usuarios encontrados
	 * @throws Exception
	 */
	public String localizarUsuario(String idSessao, String chave, String atributo) throws Exception {
		return gerenciador.localizaUsuario(idSessao, chave, atributo); 
	}
	
	/**
	 * Localiza usuarios e ordena pela distancia 
	 * @param idSessao Usuario que eh a base da pesquisa
	 * @return String contendo os usuarios ordenados pela distancia
	 * @throws Exception
	 */
	public String localizarUsuario(String idSessao) throws Exception {
		return gerenciador.usuariosOrdenadosPelaDistancia(idSessao);
	}
	
	/**
	 * Retorna o ranking dos usuario
	 * @param idSessao Id da sessao do usuario
	 * @param categoria global ou entre amigos
	 * @return Uma string contendo o ranking
	 * @throws Exception
	 */
	public String getRanking(String idSessao, String categoria) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return gerenciador.ranking(categoria, logado);
	}
	
	/**
	 * Envia Uma mensagem a amigo
	 * @param idSessao Id da sessao do usuario
	 * @param destinatario Amigo ao qual voce quer mandar a mensagem
	 * @param assunto O assunto da mensagem
	 * @param mensagem A descricao da mensagem
	 * @return O id do topico da mensagem
	 * @throws Exception
	 */
	public String enviarMensagem(String idSessao, String destinatario, String assunto, String mensagem) throws Exception {
		List<Usuario> usuarios = gerenciador.localizaRemetenteDestinatario(idSessao, destinatario);
		return usuarios.get(USUARIO_SESSAO).enviaMensagem(TipoDeMensagem.OFFTOPIC, assunto, mensagem, usuarios.get(USUARIO_PROCURADO));
	}
	
	/**
	 * O usuario da sessao idSessao envia uma mensagem de um determinado assunto para um determinado destinatario especificando
	 * o id da requisicao de um emprestimo.
	 * @param idSessao Id da sessao do usuario
	 * @param destinatario O usuario que recebera a mensagem
	 * @param assunto O assunto da mensagem
	 * @param mensagem A descricao da mensagem
	 * @param idRequisicao Id da requisicao da mensagem
	 * @return O id da mensagem 
	 * @throws Exception
	 */
	public String enviarMensagem(String idSessao, String destinatario, String assunto, String mensagem, String idRequisicao) throws Exception {
		List<Usuario> usuarios = gerenciador.localizaRemetenteDestinatario(idSessao, destinatario);
		Emprestimo emprestimo = recuperaEmprestimoPorIdRequisicao(idRequisicao);
		String idMsg = usuarios.get(USUARIO_SESSAO).enviaMensagem(TipoDeMensagem.NEGOCIACAO, assunto, mensagem, usuarios.get(USUARIO_PROCURADO));
		if(emprestimo == null) {
			throw new RequisicaoInvalidaException("Requisição de empréstimo inexistente");
		} else if(!emprestimo.getBeneficiado().equals(usuarios.get(USUARIO_SESSAO)) && !emprestimo.getItem().getDono().equals(usuarios.get(USUARIO_SESSAO))){
			throw new RequisicaoInvalidaException("O usuário não participa deste empréstimo");
		}
		return idMsg;
	}
	
	/**
	 * Le os topicos de um dado tipo.
	 * @param idSessao
	 * @param tipo
	 * @return Os topicos do tipo especificado
	 * @throws Exception
	 */
	public String lerTopicos(String idSessao, String tipo) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return logado.gerenteDeMsg().topicosMsg(logado, tipo);
	}
	
	/**
	 * Le as mensagens o topico com o id especificado.
	 * @param idSessao Id da sessao do usuario
	 * @param idTopico Id do topico da mensagem
	 * @return String contendo a mensagem
	 * @throws Exception
	 */
	public String lerMensagens(String idSessao, String idTopico) throws Exception {
		if(idTopico == null || idTopico.trim().equals("")) {
			throw new AssuntoInvalidoException("Identificador do tópico é inválido");
		} if (!gerenciador.mensagemExiste(idTopico)) {
			throw new Exception("Tópico inexistente");
		}
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		if (!gerenciador.podeLerMensagem(logado, idTopico)) {
			throw new Exception("O usuário não tem permissão para ler as mensagens deste tópico");
		}
		return Visualizador.vizualizaMensagensPorId(logado, idTopico);
	}

	/**
	 * Retorna as mensagens do usuario
	 * @param idSessaoAberta Id da sessao do usuario
	 * @return Uma string contendo as mensagens do usuario
	 * @throws SessaoInvalidaException
	 */
	public String retornarMensagens(String idSessaoAberta) throws SessaoInvalidaException{
		Usuario logado =  gerenciador.localizaUsuarioPorSessao(idSessaoAberta);
		return Visualizador.visualizaMensagens(logado);
	}
	
	/**
	 * Metodo que visualiza o historico de atividades de um Usuario
	 * @param idSessaoAberta Id da sessao do Usuario
	 * @return Uma string contendo o historico de atividades do usuario
	 * @throws SessaoInvalidaException
	 */
	public String historicoAtividades(String idSessaoAberta) throws SessaoInvalidaException {
		Usuario logado =  gerenciador.localizaUsuarioPorSessao(idSessaoAberta);
		return logado.getHistoricoDeAtividades().historicoAtividades(logado);
	}
	
	/**
	 * Metodo que visualiza o historico em conjunto de um Usuario
	 * @param idSessaoAberta Id da sessao do Usuario
	 * @return Uma string contendo o historico conjunto do Usuario
	 * @throws SessaoInvalidaException
	 */
	public String historicoAtividadesConjunto(String idSessaoAberta) throws SessaoInvalidaException {
		Usuario logado =  gerenciador.localizaUsuarioPorSessao(idSessaoAberta);
		return logado.getHistoricoDeAtividades().historicoConjunto(logado);
	}
	
	/**
	 * Metodo que publica um pedido
	 * @param idSessaoAberta Id da sessao do Usuario
	 * @param nomeItem Nome do Item
	 * @param descricaoItem Descricao do Item
	 * @return Id do pedido
	 * @throws Exception
	 */
	public String publicarPedido(String idSessaoAberta, String nomeItem, String descricaoItem) throws Exception {
		Usuario logado =  gerenciador.localizaUsuarioPorSessao(idSessaoAberta);
		logado.publicarPedido(nomeItem, descricaoItem);
		return gerenciador.adicionarPedido(logado, nomeItem);
	}
	
	/**
	 * Metodo onde um usuario oferece um item
	 * @param idSessaoAberta Id da sessao do usuario
	 * @param idPublicacaoPedido Id da publicacao do pedido
	 * @param idItem Id do item
	 * @throws Exception
	 */
	public void oferecerItem(String idSessaoAberta, String idPublicacaoPedido, String idItem) throws Exception {
		if (idPublicacaoPedido == null || idPublicacaoPedido.trim().isEmpty()) {
			throw new Exception("Identificador da publicação de pedido é inválido");
		} 
		Usuario logado =  gerenciador.localizaUsuarioPorSessao(idSessaoAberta);
		gerenciador.oferecerItem(logado, idItem, idPublicacaoPedido);
	}
	
	/**
	 * Metodo que republica um pedido
	 * @param idSessao Id da sessao do usuario
	 * @param idPublicacaoPedido Id da publicacao do pedido
	 * @throws Exception
	 */
	public void rePublicarPedido(String idSessao, String idPublicacaoPedido) throws Exception {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		logado.rePublicarPedido(gerenciador.pedidos().get(idPublicacaoPedido).getUsuario().getNome(), gerenciador.pedidos().get(idPublicacaoPedido).getItem());
	}
	
	/**
	 * Metodo que mostra as publicacoes de pedidos
	 */
	public void mostraPublicacaoDePedidos() {
		Visualizador.visualizaPublicacoesDePedidos();
	}
	
	/**
	 * Mostra os itens que o usuario emprestou
	 * @param idSessao Id da sessao do usuario
	 * @throws SessaoInvalidaException
	 */
	public void mostraItensQueEmprestou(String idSessao) throws SessaoInvalidaException{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		Visualizador.visualizaItensQueEstaEmprestado(logado);
	}
	
	/**
	 * Verifica as solicitacoes de amizades de um usuario
	 * @param idSessao do usuario
	 * @return String contendo os usuarios que solicitaram a amizade
	 * @throws SessaoInvalidaException 
	 * @throws Exception
	 */
	public String getRequisicoesDeAmizade(String idSessao) throws SessaoInvalidaException  {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.retornaSolicitacoesDeAmizade(logado);
	}
		
	/**
	 * Verifica as requisicoes de item
	 * @param idSessao do usuario
	 * @return String contendo as solicitacoes de itens feitos pelo amigos
	 * @throws SessaoInvalidaException
	 */
	public String getRequisicoesDeitem(String idSessao) throws SessaoInvalidaException{
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.retornaSolicitacoesDeItens(logado);
	}
	
	/**
	 * Verifica as notificacoes de devolucao de item
	 * @param idSessao Id da sessao do usuario
	 * @return String com as notificacoes de devolucao feita pelos amigos
	 * @throws SessaoInvalidaException
	 */
	public String getNotificacaoDeDevolucaoItem(String idSessao) throws SessaoInvalidaException {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.retornaNotificacoesDeDevolucao(logado);
	}
	
	/**
	 * Verifica as notificacoes de requisicao de devolucao do item
	 * @param idSessao Id da sessao do usuario
	 * @return 	String com as notificacoes de requisicoes feito pelos amigos
	 * @throws SessaoInvalidaException
	 */
	public String getNotificacaoRequisicaoDeDevolucaoItem(String idSessao) throws SessaoInvalidaException {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.retornaNotificacoesDeRequisicaoDevolucao(logado);
	}
	
	/**
	 * Retorna as notificacoes de itens livres
	 * @param idSessao Id da sessao do usuario
	 * @return Uma string contendo as notificacoes de itens livress
	 * @throws SessaoInvalidaException
	 */
	public String getNotificacaoDeItemLivre(String idSessao) throws SessaoInvalidaException {
		Usuario logado = gerenciador.localizaUsuarioPorSessao(idSessao);
		return Visualizador.retornaNotificaoDeItemLivre(logado);
	}
	
	/**
	 * Implementacao do Padrao Singleton
	 * Recupera a instancia da fachada se ela ja existir, ou cria uma caso contrario.
	 * @return A instancia da fachada.
	 */
	public static Fachada getInstance() {
		if (fachada == null) {
			fachada = new Fachada();
		} return fachada;
	}
	
	/**
	 * Retorna um emprestimo pesquisado pelo id de requisicao
	 * @param idRequisicaoEmprestimo Id requisicao do emprestimo
	 * @return O emprestimo pesquisados
	 * @throws RequisicaoInvalidaException
	 */
	private Emprestimo recuperaEmprestimoPorIdRequisicao(String idRequisicaoEmprestimo) throws RequisicaoInvalidaException {
		if(idRequisicaoEmprestimo == null || idRequisicaoEmprestimo.trim().equals("")) {
			throw new RequisicaoInvalidaException("Identificador da requisição de empréstimo é inválido");
		}
		for (Usuario usuario: gerenciador.usuarios()) {
			for(Emprestimo emprestimo : usuario.gerenteDeEmprestimos().getSolicitacoesDeEmprestimo()) {
				if(String.valueOf(emprestimo.getIDRequisicao()).equals(idRequisicaoEmprestimo)) {
					return emprestimo;
				}
			} 
		} return null;
	}	
}